#include "timed.h"

/**** Handles timed functions ****/

/**
 * Creates thread for sending listing messages. 
 */
void threadListingMessage() {

  // Create variables for thread creation
  pthread_t threadSendListMsg;
  
  // Create thread to send listing message
  pthread_create (&threadSendListMsg, NULL, sendListMsg, NULL);
  
}

/**
 *  Sends a listing message every 240 seconds
 */
 
void * sendListMsg () {
  // Counts number of resources
  int countFiles;
  
  // Counts number of peers to send to
  int countPeers;
  
  // Keeps track of the number of bytes in the message
  int numBytes;
  
  // List of temporary variables
  char * tempLocalFiles;
  struct dataCache * tempDataCache;
  struct contentDir * tempContentDir;  
  
  // Continuously repeat  
  while (true) {
	
    //Sleep for 240 Seconds
    sleep(240);
	
    // Buffer to hold listing message
    char buffer[MAX_MSG];
    
    // Array of peers  
    struct peer peerArray[MAX_DIR];
    
    // Array of filenames
    char * files[MAX_LISTING];  
    
    // Create iterators
    struct node * itLocalFiles = createIterator(listLocalFiles);
    struct node * itDataCache = createIterator(dataCacheList);
    struct node * itContentDir = createIterator(contentDirectoryList);  
    
  
    // Temporary Nodes for garbage collection
    struct node * itTemp1 = itLocalFiles;
    struct node * itTemp2 = itDataCache;
    struct node * itTemp3 = itContentDir;
  
    // initialize counters   
    int countFiles = 0;    
    int countPeers = 0;
    int numBytes = 0;
    
    // loop variable
    int i;
    
    // tests if entry is unique
    int uniqueEntry;
    
    // tests if peer is unique
    int uniquePeer;
    
    // Scan through local files
    while (hasNext(itLocalFiles) ){
      
      //Start off with true to handle zero case
      uniqueEntry = true;
    
      next(&itLocalFiles);
      
      //gets string from current node
      tempLocalFiles = (char * ) (itLocalFiles->payload);
      
      //tests if name is unique
      for (i = 0; i < countFiles; i++) {
        //if string is contained in list than do not add
        if (!strcmp(tempLocalFiles, files[i])){
          uniqueEntry = false;
        }
      } // End for loop
      
      // If it's a unique entry, then add it to the list
      if (uniqueEntry) {
        files[countFiles] = tempLocalFiles;
        countFiles++;
      }
      
    } // End while loop
    
    // Scan through data cache    
    while (hasNext(itDataCache)){
    
      //Start off with true to handle zero case
      uniqueEntry = true;
    
      next(&itDataCache);
      
      //gets string from current node
      tempDataCache = (struct dataCache * ) (itDataCache->payload);    
    
      //tests if name is unique
      for (i = 0; i < countFiles; i++) {
        
        //if string is contained in list than do not add
        if (!strcmp(tempDataCache->name, files[i])){
          uniqueEntry = false;
        }
      } // End for loop
      
      // If it's a unique entry, then add it to the list
      if (uniqueEntry) {
        files[countFiles] = tempDataCache->name;
        countFiles++;
      }
    
    } // End while loop
    
    // Scan through content directory
    while (hasNext(itContentDir)){
      
      //Start off with true to handle zero case
      uniqueEntry = true;
      uniquePeer = true;
    
      //Advance through list
      next(&itContentDir);
      
      //gets string from current node
      tempContentDir = (struct contentDir * ) (itContentDir->payload);    
      
      //Ensure we are only adding unique peers
      for (i = 0; i < countPeers; i++){
        if (!strcmp(ipToString(tempContentDir->address), peerArray[i].address) && (tempContentDir->port == peerArray[i].port) ) {
          uniquePeer = false;
					break;
        }
      }
      
      // If unique, then add peer
      if (uniquePeer){
        //Buffer to store IP addresses
        char * ipBuffer = (char *) malloc(20);
        strcpy( ipBuffer, ipToString(tempContentDir->address));
              
        //Increment count for peers and add peers to array      
        peerArray[countPeers].address = ipBuffer;
        peerArray[countPeers].port = tempContentDir->port;
        countPeers++;
      }
      
      //tests if name is unique
      for (i = 0; i < countFiles; i++) {
        
        //if string is contained in list than do not add
        if (!strcmp(tempContentDir->name, files[i])){
          uniqueEntry = false;
        }
      } // End for loop
      
      // If it's a unique entry, then add it to the list
      if (uniqueEntry) {
        files[countFiles] = tempContentDir->name;
        countFiles++;
      }
    } // End while loop
    
    // Free nodes created during createIterator
    free(itTemp1);
    free(itTemp2);
    free(itTemp3);
    
    //Test if local file and datacache loops work
    if (debug) {
      for (i = 0; i < countFiles; i++){
        printf("\nRESOURCE: %s", files[i]);
      }
      printf("\n");
        
      //If we have something to send, then send message.  if not, do nothing
      if (countFiles) {
        for (i = 0; i < countPeers; i++){
          printf("PEER %d: %s\n", i, peerArray[i].address);
        }
      
      }
    } // End debug if
    
    // Only create listing message if content not empty    
    if (countFiles){
    
      //Create message
      int msgLen = createListingMessage(buffer, countFiles, files);
    
      //Send message
      for (i = 0; i < countPeers; i++){
        sendUDP(peerArray[i].address, peerArray[i].port, socketfd, (unsigned char *)buffer, msgLen);
        
        //Free malloc'd IP address buffer
        free(peerArray[i].address);
      }
    }
  } // End infinite while loop
}  // End sendListMsg

/**
 * Create and send initial listing messages to all peers typed in the command-line.
 *
 */ 
void sendInitialListingMessage() {

	char buffer[MAX_MSG];
	
	// Counts number of resources
  int countFiles = 0;
  
  // Counts number of peers to send to
  int countPeers = 0;
  
  // Keeps track of the number of bytes in the message
  int numBytes = 0;
  
  // List of temporary variables
  char * tempLocalFiles;
  struct peer * tempPeer; 
	
	// Array of peers  
  struct peer peerArray[MAX_DIR];
    
  // Array of filenames
  char * files[MAX_LISTING];  
    
  // Create iterators
  struct node * itLocalFiles = createIterator(listLocalFiles);
	struct node * initialPeersIterator = createIterator(listInitialPeers);
	
	// For freeing up memory.
  struct node * itTemp1 = itLocalFiles;
	struct node * itTemp2 = initialPeersIterator;
    
  // loop variable
  int i;
    
  // tests if entry is unique
  int uniqueEntry;
    
  // tests if peer is unique
  int uniquePeer;
	
	// Scan through local files
  while (hasNext(itLocalFiles) ){
      
    //Start off with true to handle zero case
    uniqueEntry = true;
    
    next(&itLocalFiles);
      
    //gets string from current node
    tempLocalFiles = (char * ) (itLocalFiles->payload);
      
    //tests if name is unique
    for (i = 0; i < countFiles; i++) {
      //if string is contained in list than do not add
      if (!strcmp(tempLocalFiles, files[i])){
        uniqueEntry = false;
      }
    } // End for loop
      
    // If it's a unique entry, then add it to the list
    if (uniqueEntry) {
      files[countFiles] = tempLocalFiles;
      countFiles++;
    }
      
  } // End while loop
	
	// Free nodes created during createIterator
  free(itTemp1);
		
	// Scan through the initial peer list given from the command line.
  while (hasNext(initialPeersIterator)) {
      
		//Start off with true to handle zero case
    uniqueEntry = true;
    uniquePeer = true;
    
    //Advance through list
    next(&initialPeersIterator);
      
    //Gets string from current node
    tempPeer = (struct peer * ) (initialPeersIterator->payload);    
      
    //Ensure we are only adding unique peers
    for (i = 0; i < countPeers; i++){
      if (!strcmp(tempPeer->address, peerArray[i].address) && (tempPeer->port == peerArray[i].port) ) {
        uniquePeer = false;
				break;
      }
    }
      
    // If unique, then add peer
    if (uniquePeer) {
      //Buffer to store IP addresses
      char * ipBuffer = (char *) malloc(20);
      strcpy(ipBuffer, tempPeer->address);
              
      //Increment count for peers and add peers to array      
      peerArray[countPeers].address = ipBuffer;
      peerArray[countPeers].port = tempPeer->port;
      countPeers++;
    }
  } // End while loop
	
	free(itTemp2);
	
	// Only create listing message if content not empty    
  if (countFiles) {
    
    //Create message
    int msgLen = createListingMessage(buffer, countFiles, files);
    
    //Send message
    for (i = 0; i < countPeers; i++){
      sendUDP(peerArray[i].address, peerArray[i].port, socketfd, (unsigned char *) buffer, msgLen);
        
      //Free malloc'd IP address buffer
      free(peerArray[i].address);
    }
  }
}


/**
 *  Creates a listing message given an array of File Names
 */
int createListingMessage(char * buffer, int totalFiles, char * files[]){
  
  // Loop variable
  int i = 0; 
  
  // Tracks the size of the message
  int currentIndex = 0;
  
  // Populate header information
  buffer[0]= 0xCC;
  buffer[1]= 0x4C;
  unsigned short * count = (unsigned short *) &buffer[2];
  *count = htons(totalFiles);
  
  // Increment currentIndex
  currentIndex += 4;
  
  // Populate message body
  for (i = 0; i < totalFiles; i++) {
    strcpy(&buffer[currentIndex], files[i]);
    currentIndex += strlen(files[i]);
    buffer[currentIndex] = 0;
    currentIndex ++;
  }
  
  if (debug){
    printMessage((unsigned char *)buffer, currentIndex);
  }
  return currentIndex;
}


/**
 * Creates thread for discarding old requests
 */
void checkRequests() {

  // Create variables for thread creation
  pthread_t threadCheckRequests;
  
  // Create thread to check requests queue
  pthread_create (&threadCheckRequests, NULL, checkOutstandingRequests, NULL);
  
}

/**
 * Checks all requests and deletes those that are over 5 seconds old.
 */
 
void * checkOutstandingRequests(){
  
  // Temp struct for retrieving data from node
  struct outstandingReq * tempReq; 
  
  // Stores previous node in call to next
  struct node * tempNode;
  
  // Loop continuously and run every 5 seconds
  while (true){
  
    //Creates iterator over outstanding request queue
    struct node * itOutstandingReq = createIterator(outstandingRequestsList);
    struct node * tempIt = itOutstandingReq;
    
    // Iterate through request list
    while (hasNext(itOutstandingReq)) {
      
      //Store node prior to current node
      tempNode = next(&itOutstandingReq);
      
      tempReq = (struct outstandingReq *) (itOutstandingReq->payload);
      time_t currentTime = time(NULL);
      
      if (debug){
        printf("\nCURRENT TIME: %ld", currentTime);
        printf("\nNODE TIME: %ld", tempReq->timeCreated);
        
        printf("\nDELTA: %ld", currentTime - tempReq->timeCreated);
      }
      
      if ((currentTime - (tempReq->timeCreated) ) > 5){
			
        printf("\nContent: %s\nNOT FOUND\n", tempReq->name);
        //Remove node from request list
        (outstandingRequestsList.size)--;
        
        // If node is at head then re point head
        if( itOutstandingReq == outstandingRequestsList.head ){
          outstandingRequestsList.head = outstandingRequestsList.head->next;
        }
        
        // If node is at tail, remove tail and reassign tail.
        else if( itOutstandingReq == outstandingRequestsList.tail ){
          tempNode->next = NULL;
          outstandingRequestsList.tail = tempNode;
        }
        
        //If node is in middle, then skip over it.
        else {
          tempNode->next = itOutstandingReq->next;        
        }
      }
    } // End while
   
    free(tempIt);
  
    // Sleep for 5 seconds
    sleep(2);
  }
}